#import "HedronCore.h"
#import "SQLiteCore.h"
#import "LongArray.h"
#import "HTTPServer.h"

@implementation HedronCore
//_________________________________________ implementation
+ (void)load{}
+ (void)initialize{}
+ (id)new{	
    return nil;
}
+ (id)allocWithZone:(NSZone *)zone{	
    return nil;
}
+ (id)alloc{	
    return nil;
}

//_________________________________________ statics
static NSMutableArray * stocks;
static NSMutableArray * stocksIndex;
static LongArray * stocksIndexOffsets;

static Order * contextOrder;
static Order* currentEditableOrder;

static NSMutableArray * reportStocks;
static NSMutableArray * reportStocksHeaders;

static NSMutableArray * reportOrders;
static NSMutableArray * reportOrdersHeaders;

static NSDateFormatter * dateFormat;


static HTTPServer * server;
//_________________________________________ static messages
+(void) init{
	contextOrder = [[Order alloc] init];
	
	dateFormat = [[NSDateFormatter alloc] init];
	[dateFormat setDateFormat:@"yyyy-MM-dd"];
}

+(void) done{
	[dateFormat release];
	[contextOrder release];
	[HedronCore releaseStocks];
	[HedronCore releaseReports];
	[HedronCore stopHTTP];
}

+(void)releaseStocks{
	[stocks removeAllObjects];
	[stocks release];
	stocks = nil;	
	
	[stocksIndex removeAllObjects];
	[stocksIndex release];
	stocksIndex = nil;
	
	[stocksIndexOffsets release];
	stocksIndexOffsets = nil;
}

+(void) releaseReports{
	if(reportStocks != nil){
		for(int i = [reportStocks count]-1;i>=0;i--){
			NSMutableArray* array = (NSMutableArray*)[reportStocks objectAtIndex:i];
			[array removeAllObjects];
		}
		[reportStocks removeAllObjects];
		[reportStocks release];
		reportStocks = nil;	
		
		[reportStocksHeaders removeAllObjects];
		[reportStocksHeaders release];
		reportStocksHeaders = nil;
	}
	if(reportOrders != nil){
		for(int i = [reportOrders count]-1;i>=0;i--){
			NSMutableArray* array = (NSMutableArray*)[reportOrders objectAtIndex:i];
			[array removeAllObjects];
		}
		[reportOrders removeAllObjects];
		[reportOrders release];
		reportOrders = nil;	
		
		[reportOrdersHeaders removeAllObjects];
		[reportOrdersHeaders release];
		reportOrdersHeaders = nil;
	}
}

+(void) newStock:(NSString *) stockName{
	sqlite3_stmt* statement = [SQLiteCore prepare: "INSERT INTO hstock(sname) VALUES(?)"];
	if(statement){
		sqlite3_bind_text(statement, 1,[stockName UTF8String],-1,SQLITE_STATIC);
        [SQLiteCore ddl:statement];
		[SQLiteCore close:statement];
		[SQLiteCore commit];
		[HedronCore releaseStocks];
	}
}

+(void) updateStock:(long)stockId name:(NSString *) stockName{
	if(stockId>0){
		sqlite3_stmt* statement = [SQLiteCore prepare: "UPDATE hstock SET sname = ? WHERE sid = ?"];
		if(statement){
			sqlite3_bind_text(statement, 1,[stockName UTF8String],-1,SQLITE_STATIC);
			sqlite3_bind_int64(statement, 2, stockId);
			[SQLiteCore ddl:statement];
			[SQLiteCore close:statement];
			[SQLiteCore commit];
			[HedronCore releaseStocks];
		}	
	}
}


+(NSArray*) getStocks{
	if(stocks == nil){
		stocks = [[NSMutableArray alloc] init];
		stocksIndex = [[NSMutableArray alloc] init];
		stocksIndexOffsets = [[LongArray alloc] init];
		
		sqlite3_stmt* statement = [SQLiteCore prepare: "select sid, sname FROM hstock ORDER BY sname"];
		if(statement){
			unichar last = 0;
			unichar c;
			
			while([SQLiteCore next:statement]){
				long sid = sqlite3_column_int(statement, 0);
				NSString *stockname = [SQLiteCore getString:statement index:1];
				Stock* stock = [[Stock alloc]  init];
				[stock sid:sid sname:stockname];
				[stocks addObject:stock];
				
				if([stockname length] > 0){
					c = [stockname characterAtIndex:0];
					if(c != last){
						[stocksIndex addObject: [stockname substringToIndex:1]];
						[stocksIndexOffsets add: [stocks count]-1];
						last = c;
					}
				}
				[stockname release];
				[stock release];
			}
			[stocksIndexOffsets add: [stocks count]-1];
			[SQLiteCore close:statement];
		}
	}
	return stocks;
}

+(NSArray*) getStocksIndex {
	if(stocks == nil){
		[HedronCore getStocks];
	}
	return stocksIndex;
}

+(long) getStockOffset: (long) ix {
	if(stocks == nil){
		[HedronCore getStocks];
	}
	return [stocksIndexOffsets get: ix];
}

+(Order*) getContextOrder{
	return [contextOrder retain];
}

+(Order*) getCurrentEditableOrder{
	if(currentEditableOrder){
		return [currentEditableOrder retain];
	}else{
		return nil;
	}
}
+(void) setCurrentEditableOrder: (Order*)order{
	if(currentEditableOrder) [currentEditableOrder release];
	currentEditableOrder = order;
	if(order){
		[order reload];
	}
	if(currentEditableOrder) [currentEditableOrder retain];
}


+(NSArray*) getReportSocks {
	if(reportStocks == nil){
		reportStocks = [[NSMutableArray alloc] init];
		long lastDate = 0;
		NSMutableArray * currentDay;
		
		sqlite3_stmt* statement = [SQLiteCore prepare: "select shipdate, sname, SUM(qty), SUM(price) FROM hstock s, horderstock os, horder o WHERE s.sid = os.sid AND o.oid = os.oid GROUP BY shipdate, sname ORDER BY shipdate DESC, sname ASC"];
		if(statement){
			while([SQLiteCore next:statement]){
				long odate = sqlite3_column_int(statement, 0);
				if(odate != lastDate){
					currentDay = [[NSMutableArray alloc] init];
					[reportStocks addObject: currentDay];
					[currentDay release];
					lastDate = odate;
				}
				
				Stock* stock = [[Stock alloc] init];
				stock.sid = odate;
				stock.sname = [SQLiteCore getString:statement index:1];
				stock.qty = sqlite3_column_int(statement, 2);
				stock.price = sqlite3_column_double(statement, 3);
				[currentDay addObject:stock];
				[stock release];
			}
			[SQLiteCore close:statement];
		}
		
		reportStocksHeaders = [[NSMutableArray alloc] init];
		for (NSArray* stocks in reportStocks){
			double total = 0;
			long date = 0;
			for (Stock* stock in stocks){
				total += stock.price;
				date = stock.sid;
			}
			NSString *dateString = [HedronCore dateFormat: date];
			[reportStocksHeaders addObject: [NSString stringWithFormat: @"%@     %.2f", dateString, total]];
		}	
	}
	
	return reportStocks;
}

+(NSString*) getReportStocksHeader:(int) i{
	if(reportStocksHeaders){
		return [reportStocksHeaders objectAtIndex: i];
	}
	return @"";
}


+(NSArray*) getReportOrders {
	if(reportOrders == nil){
		reportOrders = [[NSMutableArray alloc] init];
		long lastDate = 0;
		NSMutableArray * currentDay;
		
		sqlite3_stmt* statement = [SQLiteCore prepare: "select shipdate, odate, oid, ototal, ocustomerid, ocustomer FROM horder ORDER BY shipdate DESC, ocustomer ASC"];
		if(statement){
			while([SQLiteCore next:statement]){
				long shipdate = sqlite3_column_int64(statement, 0);
				if(shipdate != lastDate){
					currentDay = [[NSMutableArray alloc] init];
					[reportOrders addObject: currentDay];
					[currentDay release];
					lastDate = shipdate;
				}
				
				Order* order = [[Order alloc] init];
				order.shipdate = shipdate;
				order.odate = sqlite3_column_int64(statement, 1);
				order.oid = sqlite3_column_int64(statement, 2);
				order.ototal = sqlite3_column_double(statement, 3);
				order.ocustomerid = sqlite3_column_int64(statement, 4);
				order.ocustomer = [SQLiteCore getString:statement index:5];
				[currentDay addObject:order];
				[order release];
			}
			[SQLiteCore close:statement];
		}
	
		reportOrdersHeaders = [[NSMutableArray alloc] init];
		for (NSArray* orders in reportOrders){
			double total = 0;
			long date = 0;
			for (Order* order in orders){
				total += order.ototal;
				date = order.shipdate;
			}
			
			NSString *dateString = [HedronCore dateFormat: date];
			[reportOrdersHeaders addObject: [NSString stringWithFormat: @"%@     %.2f", dateString, total]];
		}		
	}
	
	return reportOrders;
}


+(bool) deleteOrder: (Order*)order{
	if(!order){
		return NO;
	}
	long oid = order.oid;
	if(!oid){
		return NO;
	}
	if(![SQLiteCore beginTransaction]){
		return NO;
	}
	//delete order Items
	sqlite3_stmt* statement = [SQLiteCore prepare: "DELETE FROM horderstock WHERE oid = ?"];
	if(statement){
		sqlite3_bind_int64(statement, 1,  oid);
		if(![SQLiteCore ddl:statement]){
			[SQLiteCore close:statement];
			[SQLiteCore rollback];
			return NO;
		}
		[SQLiteCore close:statement];
	}else{
		[SQLiteCore rollback];
		return NO;
	}
	
	//delete order itself
	statement = [SQLiteCore prepare: "DELETE FROM horder WHERE oid = ?"];
	if(statement){
		sqlite3_bind_int64(statement, 1,  oid);
		if(![SQLiteCore ddl:statement]){
			[SQLiteCore close:statement];
			[SQLiteCore rollback];
			return NO;
		}
		[SQLiteCore close:statement];
	}else{
		[SQLiteCore rollback];
		return NO;
	}
	
	[SQLiteCore commit]; 
	
	[HedronCore releaseReports];
	return YES;
}

+(NSString*) getReportOrdersHeader:(int) i{
	if(reportOrdersHeaders){
		return [reportOrdersHeaders objectAtIndex: i];
	}
	return @"";
}

+(NSString*)dateFormat:(long) date{
	NSDate* day = [NSDate dateWithTimeIntervalSince1970: date];
	NSString *dateString = [dateFormat stringFromDate: day];
	return dateString;
}

+(long) dateFlat:(long) date{
	long result;
	//round datetime in millis to just-the-day
	NSDate *day = [NSDate dateWithTimeIntervalSince1970: date];
	
	NSDate *beginningOfDay = nil;
	//Find the absolute beginning of today, and the time interval of each day
	NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
	[calendar rangeOfUnit:NSDayCalendarUnit startDate:&beginningOfDay
				 interval:nil forDate:day];
	
	result = [beginningOfDay timeIntervalSince1970];
	
	return result;
}



+(BOOL)startHTTP{
	if(!server){
		server = [[HTTPServer alloc] init];	
		[server start];	
	}
	return server != nil;
}

+(BOOL)isHTTPStarted{
	return server != nil;
}


+(NSString *)HTTPMessage{
	if(server){
		return server.message;
	}else{
		return @"";
	}
}

+(BOOL)stopHTTP{
	if(server){
		[server stop];
		[server dealloc];
		server = nil;
		return YES;
	}else{
		return NO;
	}
}

@end



@implementation Stock
@synthesize sid, sname, qty, price;

-(id) sid:(long)stockId sname:(NSString *) stockName{
	self.sid = stockId;
	self.sname = [stockName retain];
	return self;
}

-(void) updateStockName{
	[HedronCore updateStock:sid name:sname];
}

- (void)dealloc {
	[sname release];
    [super dealloc];
}
@end



@implementation Order
@synthesize oid, odate, shipdate, ocustomerid, ototal, ocustomer;

-(Order*)init {
	oid = 0;
	date = 0;
	ototal = 0;
	ocustomerid = 0;
	ocustomer = @"-- Изберете клиент --";
	stocks = [[NSMutableArray alloc] init];
    return self;
}

- (void)dealloc {
	[stocks removeAllObjects];
	[stocks dealloc];
	[ocustomer release];
    [super dealloc];
}

-(void) addStock:(Stock*)stock{
	bool exists = NO;
	for(int i=[stocks count]-1; i>=0;i--){
		if(((Stock*)[stocks objectAtIndex:i]).sid == stock.sid){
			exists = YES;
			break;
		}
	}
	if(!exists){
		[stocks addObject:stock];
	}
}

-(NSArray*) getStocks{
	return [stocks retain];
}

-(bool) save{
	[self updateTotalAndClear];
	if(![SQLiteCore beginTransaction]){
		return NO;
	}
	long newID;
	sqlite3_stmt* statement;
	
	if (oid > 0) {
		newID = oid;
		// update order
		statement = [SQLiteCore prepare: 
			"UPDATE horder SET ocustomerid = ?, ocustomer = ?, ototal = ?, shipdate = ? WHERE oid = ?"];
		if(statement){
			sqlite3_bind_int64(statement, 1, ocustomerid);
			sqlite3_bind_text(statement, 2,[ocustomer UTF8String],-1,SQLITE_STATIC);
			sqlite3_bind_double(statement, 3, ototal);
			sqlite3_bind_int64(statement, 4, shipdate);
			sqlite3_bind_int64(statement, 5, oid);
			if(![SQLiteCore ddl:statement]){
				[SQLiteCore close:statement];
				[SQLiteCore rollback];
				return NO;
			}
			[SQLiteCore close:statement];
		}else{
			[SQLiteCore rollback];
			return NO;
		}
		
		//delete order Items
		statement = [SQLiteCore prepare: "DELETE FROM horderstock WHERE oid = ?"];
		//horderstock ( oid INTEGER, sid INTEGER, qty INTEGER, price DOUBLE)
		if(statement){
			sqlite3_bind_int64(statement, 1,  oid);
			if(![SQLiteCore ddl:statement]){
				[SQLiteCore close:statement];
				[SQLiteCore rollback];
				return NO;
			}
			[SQLiteCore close:statement];
		}else{
			[SQLiteCore rollback];
			return NO;
		}
		

	}else{
		//add new order
		
		//round datetime in millis to just-the-day
		NSDate *today = [[NSDate alloc] init];
		odate = [HedronCore dateFlat: [today timeIntervalSince1970]];
		[today release];
		
		statement = [SQLiteCore prepare: "INSERT INTO horder (ocustomerid, ocustomer, ototal, odate, shipdate) VALUES(?,?,?,?,?)"];
		if(statement){
			sqlite3_bind_int64(statement, 1,  ocustomerid);
			sqlite3_bind_text(statement, 2,[ocustomer UTF8String],-1,SQLITE_STATIC);
			sqlite3_bind_double(statement, 3,  ototal);
			sqlite3_bind_int64(statement, 4,  odate);
			sqlite3_bind_int64(statement, 5,  shipdate);
			if(![SQLiteCore ddl:statement]){
				[SQLiteCore close:statement];
				[SQLiteCore rollback];
				return NO;
			}
			[SQLiteCore close:statement];
		}else{
			[SQLiteCore rollback];
			return NO;
		}	
		
		newID = [SQLiteCore lastROWID];
	}
	
	for (int i = [stocks count]-1; i>=0; i--) {
		//insert order items
		Stock *stock = [stocks objectAtIndex:i];
		statement = [SQLiteCore prepare: "INSERT INTO horderstock (oid,sid,qty,price) VALUES(?,?,?,?)"];
		if(statement){
			sqlite3_bind_int64(statement, 1,  newID);
			sqlite3_bind_int64(statement, 2,  stock.sid);
			sqlite3_bind_int64(statement, 3,  stock.qty);
			sqlite3_bind_double(statement, 4,  stock.price);
			if(![SQLiteCore ddl:statement]){
				[SQLiteCore close:statement];
				[SQLiteCore rollback];
				return NO;
			}
			[SQLiteCore close:statement];
		}else{
			[SQLiteCore rollback];
			return NO;
		}
	}
	[SQLiteCore commit];
	
	oid = newID;
	
	[HedronCore releaseReports];
	return YES;
}

-(void) clear{
	oid = 0;
	date = 0;
	shipdate = 0;
	ototal = 0;
	ocustomerid = 0;
	[ocustomer release];
	ocustomer = @"-- Изберете клиент --";
	[stocks removeAllObjects];
}

-(long) getStocksCount{
	return [stocks count];
}

-(Stock*) getStock: (int)row{
	return [stocks objectAtIndex: row];
}

-(void) updateTotalAndClear{
	ototal = 0;
	for(int i = [stocks count]-1;i>=0;i--){
		Stock* stock = (Stock*)[stocks objectAtIndex:i];
		if(stock.qty <=0){
			[stocks removeObjectAtIndex:i];
		}else{
			ototal += stock.price;
		}
	}
}

-(long*) shipdatePointer{
	return &shipdate;
}

-(void) reload{
	if(!oid) return;
	{
		sqlite3_stmt* statement = [SQLiteCore prepare: "select shipdate, odate, oid, ototal, ocustomerid, ocustomer FROM horder WHERE oid = ?"];
		if(statement){
			sqlite3_bind_int64(statement, 1,  oid);

			if([SQLiteCore next:statement]){
				shipdate = sqlite3_column_int64(statement, 0);
				odate = sqlite3_column_int64(statement, 1);
				oid = sqlite3_column_int64(statement, 2);
				ototal = sqlite3_column_double(statement, 3);
				ocustomerid = sqlite3_column_int64(statement, 4);
				ocustomer = [[SQLiteCore getString:statement index:5] retain];
			}
			[SQLiteCore close:statement];
		}
	}
	
	[stocks removeAllObjects];
	
	{
		sqlite3_stmt* statement = [SQLiteCore prepare: "SELECT o.sid, s.sname, o.qty, o.price FROM horderstock o, hstock s WHERE o.oid = ? AND s.sid = o.sid ORDER BY s.sname "];
		if(statement){
			sqlite3_bind_int64(statement, 1, oid);
			while([SQLiteCore next:statement]){			
				Stock* stock = [[Stock alloc] init];
				stock.sid = sqlite3_column_int(statement, 0);
				stock.sname = [SQLiteCore getString:statement index:1];
				stock.qty = sqlite3_column_int(statement, 2);
				stock.price = sqlite3_column_double(statement, 3);
				[self addStock: stock];
				[stock release];
			}
			[SQLiteCore close:statement];
		}
	}
}
@end

